#pragma once
#include <JeayeSDK/JeayeSDK.h>
#include <JeayeSDK/JString.h>
#include <JeayeSDK/JPlex.h>

#ifndef _AFX
struct __POSITION
{
};
#endif
typedef __POSITION* POSITION;

#pragma push_macro("new")
#undef new
static inline void ConstructElement(JString* pNewData)
{
	if(pNewData==NULL) throw 0;
	pNewData->Initialize();
}
#pragma pop_macro("new")
static inline void DestructElement(JString* pOldData)
{
	if(pOldData==NULL) throw 0;
	pOldData->~JString();
}
static inline void CopyElement(JString* pSrc, JString* pDest)
{
	if(pSrc==NULL || pDest==NULL) throw 0;
	*pSrc = *pDest;
}

class JEAYESDK_API JStringList
{
protected:
	struct CNode
	{
		CNode* pNext;
		CNode* pPrev;
		JString data;
	};

public:
	JStringList(INT_PTR nBlockSize = 10);
	~JStringList(void);
	inline INT_PTR GetCount() const{ return m_nCount; }
	INT_PTR GetSize() const{ return m_nCount; }
	BOOL IsEmpty() const{ return m_nCount == 0; }

	// peek at head or tail
	inline JString& GetHead(){ 
		_ASSERT(m_pNodeHead != NULL);
		return m_pNodeHead->data; 
	}
	inline const JString& GetHead() const{ 
		_ASSERT(m_pNodeHead != NULL);
		return m_pNodeHead->data; 
	}
	JString& GetTail(){ 
		_ASSERT(m_pNodeTail != NULL);
		return m_pNodeTail->data; 
	}
	const JString& GetTail() const{ 
		_ASSERT(m_pNodeTail != NULL);
		return m_pNodeTail->data; 
	}

	// Operations
	// get head or tail (and remove it) - don't call on empty list!
	JString RemoveHead();
	JString RemoveTail();

	// add before head or after tail
	POSITION AddHead(LPCTSTR newElement);
	POSITION AddTail(LPCTSTR newElement);

	POSITION AddHead(const JString& newElement);
	POSITION AddTail(const JString& newElement);


	// add another list of elements before head or after tail
	void AddHead(JStringList* pNewList);
	void AddTail(JStringList* pNewList);

	// remove all elements
	void RemoveAll();

	// iteration
	POSITION GetHeadPosition() const{ return (POSITION) m_pNodeHead; }
	POSITION GetTailPosition() const{ return (POSITION) m_pNodeTail; }
	JString& GetNext(POSITION& rPosition){ 
		CNode* pNode = (CNode*) rPosition;
		_ASSERT(pNode!=0);
		rPosition = (POSITION) pNode->pNext;
		return pNode->data;
	}
	const JString& GetNext(POSITION& rPosition) const{ 
		CNode* pNode = (CNode*) rPosition;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		rPosition = (POSITION) pNode->pNext;
		return pNode->data; 
	}
	JString& GetPrev(POSITION& rPosition){ 
		CNode* pNode = (CNode*) rPosition;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		rPosition = (POSITION) pNode->pPrev;
		return pNode->data; 
	}
	const JString& GetPrev(POSITION& rPosition) const{ 
		CNode* pNode = (CNode*) rPosition;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		rPosition = (POSITION) pNode->pPrev;
		return pNode->data; 
	}

	// getting/modifying an element at a given position
	JString& GetAt(POSITION position){
		CNode* pNode = (CNode*) position;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		return pNode->data; }
	const JString& GetAt(POSITION position) const{ 
		CNode* pNode = (CNode*) position;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		return pNode->data;
	}
	void SetAt(POSITION pos, LPCTSTR newElement){ 
		CNode* pNode = (CNode*) pos;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		pNode->data = newElement; 
	}

	void SetAt(POSITION pos, const JString& newElement){ 
		CNode* pNode = (CNode*) pos;
		_ASSERT(pNode!=0);
		if( pNode == NULL ) throw 0;
		pNode->data = newElement; 
	}
	void RemoveAt(POSITION position);

	// inserting before or after a given position
	POSITION InsertBefore(POSITION position, LPCTSTR newElement);
	POSITION InsertAfter(POSITION position, LPCTSTR newElement);

	POSITION InsertBefore(POSITION position, const JString& newElement);
	POSITION InsertAfter(POSITION position, const JString& newElement);


	// helper functions (note: O(n) speed)
	POSITION Find(LPCTSTR searchValue, POSITION startAfter = NULL) const;
	// defaults to starting at the HEAD
	// return NULL if not found
	POSITION FindIndex(INT_PTR nIndex) const;
	// get the 'nIndex'th element (may return NULL)

	// Implementation
protected:
	CNode* m_pNodeHead;
	CNode* m_pNodeTail;
	INT_PTR m_nCount;
	CNode* m_pNodeFree;
	struct JPlex* m_pBlocks;
	INT_PTR m_nBlockSize;

	CNode* NewNode(CNode* pPrev, CNode* pNext);
	void FreeNode(CNode*);
};

